﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Web;
using FaceSharp.Api.GraphApi;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using FaceSharp.Api.RestApi;

namespace FaceSharp.Api.Objects
{

    /// <summary>
    /// This class provides commonly used user information. 
    /// 
    /// See Facebook's documentation for the entire set of information availalbe.
    /// http://developers.facebook.com/docs/reference/api/user/
    /// </summary>
    public class User : GraphBase
    {
        private IFacebookCore _facebookCore;
        private readonly RestBase _restBase;

        public User(IFacebookCore facebookCore)
            : base(facebookCore)
        {
            _facebookCore = facebookCore;
            _restBase = new RestBase(facebookCore);            
        }

        public enum ProfilePictureSize
        {
            /// <summary>
            /// 50px width -retains original aspect ratio
            /// </summary>
            Small,  
            
            Normal, 

            /// <summary>
            /// original photo
            /// </summary>
            Large, 

            /// <summary>
            /// 50px x 50px
            /// </summary>
            Square 
        }


        /// <summary>
        /// Gets the actual URL for the Facebook user's photo. 
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="pictureSize"></param>
        /// <returns></returns>
        public String GetProfilePhoto(long userId,ProfilePictureSize pictureSize = ProfilePictureSize.Normal)
        {
            RestSharp.RestClient client;
            RestSharp.RestRequest request;
            GenericGraphRequest(userId + "/picture", new Dictionary<string, string>() { { "type", pictureSize.ToString().ToLower() } }, out client, out request);
            client.FollowRedirects = false;
            var response = client.Execute(request);
            return response.StatusCode == HttpStatusCode.Found ? response.Headers.Where(x => x.Name == "Location").Select(x => x.Value.ToString()).FirstOrDefault() : "";
        }


        /// <summary>
        /// Returns the current user's facebook id.
        /// </summary>
        /// <returns></returns>
        public long GetCurrentUserId()
        {
            Trace.Write("Begin Request", "GraphApi.GetCurrentUserId");
            long currentUserId;
            if (_facebookCore.CurrentUser != null && _facebookCore.CurrentUser.Id != 0)
            {
                Trace.Write("uid:" + _facebookCore.CurrentUser.Id, "GraphApi.GetCurrentUserId");
                currentUserId = _facebookCore.CurrentUser.Id;
                Trace.Write("Cache Hit", "GraphApi.GetCurrentUserId");
            }
            else
            {

                _facebookCore.CurrentUser = GetCurrentUser();
                currentUserId = _facebookCore.CurrentUser.Id;
                Trace.Write("Cache Miss", "GraphApi.GetCurrentUserId");
            }

            Trace.Write("End Request", "GraphApi.GetCurrentUserId");
            return currentUserId;
        }

        /// <summary>
        /// Please consider using GetCurrentUserId if you only want to get the user id, this call hits facebook each time.
        /// </summary>
        /// <returns></returns>
        internal Types.User GetCurrentUserNoCore()
        {
            Trace.Write("Begin Request", "GraphApi.GetCurrentUser");
            var jObject = MakeGraphRequest("me", fields: new[] { "id", "name", "first_name", "last_name", "email", "birthday", "gender", "location", "accounts" });

            var user = new Types.User();
            if (jObject != null)
            {
                user = new Types.User()
                {
                    Id = long.Parse((string)jObject["id"]),
                    Name = (string)jObject["name"],
                    FirstName = (string)jObject["first_name"],
                    LastName = (string)jObject["last_name"]
                };

                if (jObject["email"] != null)
                {
                    user.Email = (string)jObject["email"];
                }
                else if (jObject["proxy_email"] != null)
                {
                    user.Email = (string)jObject["proxy_email"];
                }

                if (jObject["birthday"] != null)
                {
                    user.Birthday = DateTime.Parse((string)jObject["birthday"]);
                }
                if (jObject["accounts"] != null)
                {
                    user.Accounts = JsonConvert.DeserializeObject<IEnumerable<Types.User.AccountData>>(jObject["accounts"]["data"].ToString());
                }
                if (jObject["gender"] != null)
                {
                    user.Gender = (string)jObject["gender"];
                }

                if (jObject["location"] != null && !String.IsNullOrWhiteSpace((string)jObject["location"]["name"]))
                {
                    string location = (string)jObject["location"]["name"];
                    if (location.Contains(','))
                    {
                        string[] locationValues = location.Split(',');

                        user.City = locationValues[0];
                        user.StateProvince = locationValues[1].Trim();
                    }
                }
                _facebookCore.CurrentUser = user;
            }
            Trace.Write("End Request", "GraphApi.GetCurrentUser");

            return user;
        }


        /// <summary>
        /// Please consider using GetCurrentUserId if you only want to get the user id, this call hits facebook each time.
        /// </summary>
        /// <returns></returns>
        public Types.User GetCurrentUser()
        {
            Trace.Write("Begin Request", "GraphApi.GetCurrentUser");
            var user = _facebookCore.CurrentUser ?? GetCurrentUserNoCore();
            Trace.Write("End Request", "GraphApi.GetCurrentUser");
            return user;
        }


        /// <summary>
        /// Returns basic, public information (Id,Name,FirstName and LastName) for users.
        /// </summary>
        /// <param name="userIds"></param>
        /// <returns></returns>
        public IEnumerable<Types.User> GetFriends(IEnumerable<long> userIds)
        {
            var jObject = MakeGraphRequest(userIds.Select(x => x.ToString()), new[] { "id", "name", "first_name", "last_name" });
            var users = new List<Types.User>();
            if (jObject != null)
            {
                users.AddRange(
                    userIds.Select(userId => jObject[userId.ToString()]).Select(
                    user => new Types.User()
                    {
                        Id = long.Parse((string)user["id"]),
                        Name = (string)user["name"],
                        FirstName = (string)user["first_name"],
                        LastName = (string)user["last_name"]
                    }));
            }
            return users;
        }


        /// <summary>
        /// Returns all the user's friends, with basic information (Id,Name,FirstName and LastName)
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Types.User> GetFriends()
        {
            Trace.Write("Begin Request", "GraphApi.GetFriends");
            var jObject = MakeGraphRequest("me", "friends", new[] { "id", "name", "first_name", "last_name" });
            var users = new List<Types.User>();
            if (jObject != null)
            {

                var jData = (JArray)jObject["data"];
                users = (from user in jData
                         select new Types.User()
                         {
                             Id = long.Parse((string)user["id"]),
                             Name = (string)user["name"],
                             FirstName = (string)user["first_name"],
                             LastName = (string)user["last_name"]
                         }).ToList();

            }
            Trace.Write("End Request", "GraphApi.GetFriends");
            
            return users.OrderBy(x => x.FirstName).ThenBy(x => x.LastName).ToList(); ;
        }


        /// <summary>
        /// Gets basic information for a given user (Id,Name,FirstName and LastName)
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public Types.User Get(long userId)
        {
            Trace.Write("Begin Request", "GraphApi.GetUserInfo");
            var cacheKey = "GraphApi.GetUserInfo" + userId;
            var cache = HttpRuntime.Cache;
            var user = (Types.User)cache[cacheKey];
            if (user == null)
            {
                var jObject = MakeGraphRequest(userId.ToString(), fields: new[] { "id", "name", "first_name", "last_name" });
                if (jObject != null)
                {
                    user = new Types.User()
                    {
                        Id = long.Parse((string)jObject["id"]),
                        Name = (string)jObject["name"],
                        FirstName = (string)jObject["first_name"],
                        LastName = (string)jObject["last_name"]
                    };
                    if (jObject["email"] != null)
                    {
                        user.Email = (string)jObject["email"];
                    }
                    else if (jObject["proxy_email"] != null)
                    {
                        user.Email = (string)jObject["proxy_email"];
                    }
                    cache.Insert(cacheKey, user, null, DateTime.Now.AddMinutes(5), TimeSpan.Zero);
                }
            }
            Trace.Write("End Request", "GraphApi.GetUserInfo");
            return user;
        }

        public void SetStatus(long userId, string statusMessage)
        {
            Trace.Write("Begin Request", "GraphApi.UserSetStatus");
            _restBase.MakeRestRequest("status.set", new Dictionary<string, string>()
                                                           {
                                                             {"uid",userId.ToString()},
                                                             {"status", statusMessage}
                                                           });

            Trace.Write("End Request", "GraphApi.UserSetStatus");
        }
    }
}